package hu.mapro.flang.gwt.server.plan;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;

import org.apache.commons.lang3.time.DateUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

@SuppressWarnings("serial")
public class Plan implements Serializable {

	Map<Long, Schedule> scheduleMap = Maps.newHashMap();
	PriorityQueue<Schedule> scheduled = new PriorityQueue<Schedule>();

	List<List<Long>> queued = Lists.newArrayList();

	public static class Schedule implements Serializable, Comparable<Schedule> {
		long exerciseId;
		Date time;

		public Schedule(long exerciseId, double sleepDays) {
			super();
			this.exerciseId = exerciseId;
			sleep(sleepDays);
		}

		@Override
		public int compareTo(Schedule o) {
			return time.compareTo(o.time);
		}

		public long getExerciseId() {
			return exerciseId;
		}

		public void setExerciseId(long exerciseId) {
			this.exerciseId = exerciseId;
		}

		public Date getTime() {
			return time;
		}

		public void sleep(double sleepDays) {
			time = new Date(
					(long) (new Date().getTime() + DateUtils.MILLIS_PER_DAY
							* sleepDays));
		}

	}

	public int getQueueSize() {
		int size = 0;

		for (List<Long> ch : queued) {
			size += ch.size();
		}

		return size;
	}

	public int getScheduleSize() {
		return scheduled.size();
	}

	public void schedule(long exerciseId, double sleepDays) {
		Schedule sch = scheduleMap.get(exerciseId);

		if (sch != null) {
			scheduled.remove(sch);
			sch.sleep(sleepDays);
		} else {
			sch = new Schedule(exerciseId, sleepDays);
			scheduleMap.put(exerciseId, sch);
		}

		scheduled.add(sch);
	}

	public NextExercise getNextExercise() {
		Schedule sch = scheduled.peek();

		if (sch != null && sch.getTime().before(new Date())) {
			scheduled.remove();
			scheduleMap.remove(sch.getExerciseId());

			return new NextExercise(Source.SCHEDULE, sch.getExerciseId());
		}

		for (Iterator<List<Long>> iterator = queued.iterator(); iterator
				.hasNext();) {
			List<Long> chapter = (List<Long>) iterator.next();

			if (chapter.isEmpty()) {
				iterator.remove();
				continue;
			}

			return new NextExercise(Source.QUEUE, chapter.remove(0));
		}

		return null;
	}

//	public void queueBook(Collection<Exercise> es) {
//		int lastChapter = -1;
//		List<Long> chapter = null;
//
//		for (Exercise e : es) {
//			if (lastChapter != e.getChapter()) {
//				chapter = Lists.newArrayList();
//				queued.add(chapter);
//				lastChapter = e.getChapter();
//			}
//
//			chapter.add(e.getId());
//		}
//
//		queued.add(new ArrayList<Long>());
//
//	}

	public void toChapterEnd(long exerciseId) {
		queued.get(0).add(exerciseId);
	}

	public void toBookEnd(long exerciseId) {
		queued.get(queued.size() - 1).add(exerciseId);
	}

	public void skipChapter() {
		queued.remove(0);
	}

	public void clearQueue() {
		queued.clear();
	}

	public void clearSchedule() {
		scheduled.clear();
		scheduleMap.clear();
	}

	public Plan getPlan(byte[] bytes) {
		if (bytes != null) {
			try {
				return (Plan) new ObjectInputStream(new ByteArrayInputStream(
						bytes)).readObject();
			} catch (IOException e) {
				throw new RuntimeException(e);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
		return null;
	}

}
